package com.thd.springboottest.redisson.controller;

import com.thd.springboottest.redisson.utils.DistributeLockUtil;
import com.thd.springboottest.redisson.utils.RedissLockUtil;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * com.thd.springboottest.redisson.controller.RedissionController
 *
 * @author: wanglei62
 * @DATE: 2020/5/21 14:38
 **/
@RestController
@RequestMapping("/redission")
public class RedissionController {
    @Autowired
    private DistributeLockUtil distributeLockUtil;
    @Autowired
    private RedissonClient redissonClient;


    @GetMapping("/testList")
    public ResponseEntity testList() {
        RList rlist = redissonClient.getList("list");
        rlist.add(Math.random());


        List l = rlist.readAll();
        return ResponseEntity.ok(l);
    }


    @GetMapping("/testMap")
    public ResponseEntity testMap() {
        RMap map = redissonClient.getMap("map");
        map.put(Math.random(),Math.random());

        Map m = map.readAllMap();
        return ResponseEntity.ok(m);
    }

    @GetMapping("/testMapCache")
    public ResponseEntity testMapCache() {
        RMapCache mapCache = redissonClient.getMapCache("mapcache");
        mapCache.put(Math.random(),Math.random(),10, TimeUnit.SECONDS);
        Map m = mapCache.readAllMap();


        return ResponseEntity.ok(m);
    }

    @GetMapping("/testSet")
    public ResponseEntity testSet() {
        RSet set = redissonClient.getSet("set");
        set.add(Math.random());

        set.add(1);
        Set s = set.readAll();

        return ResponseEntity.ok(s);
    }


    @GetMapping("/testSetCache")
    public ResponseEntity testSetCache() {
        RSetCache setCache = redissonClient.getSetCache("setCache");
        setCache.add(Math.random(),5,TimeUnit.SECONDS);

        setCache.add(1);
        Set s = setCache.readAll();

        return ResponseEntity.ok(s);
    }



    // 测试分布式锁
    // url : http://127.0.0.1:8899/thd/redission/testLock
    @GetMapping("/testLock")
    public ResponseEntity testLock() {
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    distributeLockUtil.lockFor10S("LOCK");
                }
            }).start();
        }
        return ResponseEntity.ok("SUCCESS");
    }


    // 测试并发锁
    // url : http://127.0.0.1:8899/thd/redission/testConcurrentLock
    // 多个该请求同时访问，只有一个能获取到锁
    @GetMapping("/testConcurrentLock")
    public ResponseEntity testConcurrentLock() {

        RLock lock = redissonClient.getLock("LOCK");
        // 设置持有锁超时时间为-1 则为不会自动释放锁,且会自动续租持有锁的时间
//        lock.lock(-1,TimeUnit.SECONDS);
        lock.lock();
//        lock.lock() = lock.lock(-1,null)

        System.out.println(Thread.currentThread().getName() + "进入");
        System.out.println(Thread.currentThread().getName() + "上锁");
        try {
            // 睡10S
            Thread.sleep(60000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + "释放锁");
        lock.unlock();
        System.out.println(Thread.currentThread().getName() + "离开");
        return ResponseEntity.ok("SUCCESS");
    }



    // 测试并发锁
    // url : http://127.0.0.1:8899/thd/redission/testConcurrentTryLock
    // 多个该请求同时访问，只有一个能获取到锁
    /*
    tryLock的标准用法：

RLock lock = redisson.getLock("xxx");
try {
    //尝试加锁，最多等待10秒，上锁以后5秒自动解锁
    if (lock.tryLock(10,5, TimeUnit.SECONDS)) {
        try {
            //处理
            logger.info("tryLock thread---{}, lock:{}", Thread.currentThread().getId(), lock);
        } catch (Exception e) {
        } finally {
            //解锁
            lock.unlock();
        }
    }
} catch (InterruptedException e) {
    //处理
    //保留中断发生的证据，以便调用栈中更高层的代码能知道中断，并对中断作出响应
    Thread.currentThread().interrupt();
}

     */
    @GetMapping("/testConcurrentTryLock")
    public ResponseEntity testConcurrentTryLock() {
        System.out.println(Thread.currentThread().getName() + "进入 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        RLock lock = redissonClient.getLock("LOCK");
        try{
            // tryLock必须放在try中,因为如果等锁时间超时会抛出异常
            /**
             * tryLock
             * 第一个参数:等待时间,再此时间范围内会不断尝试获取锁
             * 第二个参数:锁的持有时间(超时自动释放)
             * 第三个参数:时间单位
             */
            boolean b = lock.tryLock(9,-1,TimeUnit.SECONDS);

            if(b){
                System.out.println(Thread.currentThread().getName() + "上锁 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
                // =================== 以下是业务代码 =========================//
                try {
                    // 睡4S
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }


                System.out.println(Thread.currentThread().getName() + "离开 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
                return ResponseEntity.ok("SUCCESS");
                // =================== 以上是业务代码 =========================//
            }else{
                System.out.println(Thread.currentThread().getName() + "超时 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
                return ResponseEntity.status(500).body("超时");
            }
        }catch(InterruptedException e){
            System.out.println(Thread.currentThread().getName() + "打断 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
            return ResponseEntity.status(500).body("打断");
        }finally {

            if(lock.isLocked()){
                // 如果锁被当前线程持有则释放锁
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                    System.out.println(Thread.currentThread().getName() + "释放锁 成功 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
                }else{
                    System.out.println(Thread.currentThread().getName() + "释放锁 失败(非当前线程) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
                }
            }else{
                System.out.println(Thread.currentThread().getName() + "释放锁 失败(超时自动解锁) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
            }
        }



    }





    // 测试并发锁 - 上锁
    // url : http://127.0.0.1:8899/thd/redission/testConcurrentLockLock
    // 多个该请求同时访问，只有一个能获取到锁
    @GetMapping("/testConcurrentLockLock")
    public ResponseEntity testConcurrentLockLock() {
        RLock lock = redissonClient.getLock("LOCK");
        lock.lock(60,TimeUnit.SECONDS);
        System.out.println(Thread.currentThread().getId() + ":上锁");
        return ResponseEntity.ok("SUCCESS");
    }

    // 测试并发锁 - 解锁
    // url : http://127.0.0.1:8899/thd/redission/testConcurrentLockUnLock
    // 多个该请求同时访问，只有一个能获取到锁
    @GetMapping("/testConcurrentLockUnLock")
    public ResponseEntity testConcurrentLockUnLock() {
        RLock lock = redissonClient.getLock("LOCK");
        try{
            // 解锁的时候可能会抛出异常(没有上锁直接解锁所以抛出异常)所以,lock.unlock()应该放在try中
            lock.unlock();
        }catch(Exception e){
            e.printStackTrace();;
            return ResponseEntity.status(500).body(e.getMessage());
        }

        return ResponseEntity.ok("SUCCESS");
    }





    // 测试并发锁 - 锁的标准写法
    // url : http://127.0.0.1:8899/thd/redission/testStandardConcurrentLock
    // 一般情况下使用redisson锁的写法
    @GetMapping("/testStandardConcurrentLock")
    public ResponseEntity testStandardConcurrentLock() {
        RLock lock = redissonClient.getLock("MYLOCK");
        try{
            // tryLock必须放在try中,因为如果等锁时间超时会抛出异常
            /**
             * tryLock
             * 第一个参数:等待时间,再此时间范围内会不断尝试获取锁,等待10S,
             超时返回false,
             在此期间获取到锁返回true
             * 第二个参数:锁的持有时间(超时自动释放)
             -1为不自动释放且会续租,
             除非手工释放锁或线程销毁(线程销毁会自动释放锁)
             * 第三个参数:上面2个参数的时间单位
             */
            boolean b = lock.tryLock(10,-1,TimeUnit.SECONDS);

            if(b){
                System.out.println(Thread.currentThread().getName() + "上锁 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
                // =================== 以下是业务代码 =========================//
                try {
                    // 睡3S
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName() + "离开 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
                return ResponseEntity.ok("SUCCESS");
                // =================== 以上是业务代码 =========================//
            }else{
                System.out.println(Thread.currentThread().getName() + "超时 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
                return ResponseEntity.status(500).body("超时");
            }
        }catch(InterruptedException e){
            System.out.println(Thread.currentThread().getName() + "被打断 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
            return ResponseEntity.status(500).body("被打断");
        }finally {
            try{ // 用try是因为释放锁的时候必须是该线程上的锁,如果锁超期提前释放了,这里释放锁将会抛出异常
                lock.unlock();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }






}
